
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

:- consult(['mostrar', 'tests']).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%% Ejercicios %%%%

%% Ejercicio 1

% dimension(+Tablero, -N, -M).
% Asumimos que el tablero tiene forma rectangular
dimension(Tablero,0,0) :-
   length(Tablero,0).

dimension(Tablero, N,M) :-
   length(Tablero,N), N > 0,
   last(Tablero,X),
   length(X,M).

% posicion(+Tablero, -I, -J).
% Los I,J que sirven son los que cumplen estar entre 0 y N-1,
% y 0 y M-1 respectivamente
posicion(Tablero,I,J) :-
   dimension(Tablero, N, M),
   N1 is N-1,
   between(0,N1,I),
   M1 is M-1,
   between(0,M1,J).



%% Ejercicio 2

% subtablero(+Tablero, +I, +J, -Subtablero).
% para conseguir el subtablero de dimensiones I J descartamos las filas
% desde 0 a I, luego descartamos las columnas desde 0 a J en adelante
subtablero(Tablero,I,J,Sub) :-
   split(I,Tablero,_,L2),
   cortarFilas(L2,J,Sub).


% split parte una lista en 2, desde 0 hasta N-1 y desde N hasta |L|
split(N,L,L1,L2) :-
   length(L1,N),
   append(L1,L2,L).


% cortarFilas descarta las columnas de N + 1 en adelante
cortarFilas([],_,[]).
cortarFilas([X|XS],N,[Y|YS]) :-
   split(N,X,_,Y),
   cortarFilas(XS,N,YS).



%% Ejercicio 3

% transponer(+Tablero, -Transpuesto).
transponer(Tablero,T) :-
   dimension(Tablero,N,M),
   generarMatriz(T,M,N),
   N1 is N-1,
   M1 is M-1,
   chequearElementos(Tablero,T,N1,M1).


generarMatriz(Mat,N,M) :-
   length(Mat,N),
   todosDim(Mat,M).


todosDim([],_).
todosDim([X|XS],M):-
   length(X,M),
   todosDim(XS,M).


%Caso Base
chequearElementos(Tablero,T,0,0) :-
   posicion(Tablero,0,0),
   nth0(0,Tablero,X),
   nth0(0,X,Y),
   nth0(0,T,Z),
   nth0(0,Z,Y).

%Chequeamos y terminamos de recorrer las filas, reiniciamos I y decrementamos J
chequearElementos(Tablero,T,0,J) :-
   J > 0,
   nth0(0,Tablero,X),
   nth0(J,X,Y),
   nth0(J,T,Z),
   nth0(0,Z,Y),
   length(Tablero,N),
   N1 is N -1,
   J1 is J -1,
   chequearElementos(Tablero,T,N1,J1).

%Chequeamos y pasamos a la proxima fila.
chequearElementos(Tablero,T,I,J) :-
   I >0,
   nth0(I,Tablero,X),
   nth0(J,X,Y),
   nth0(J,T,Z),
   nth0(I,Z,Y),
   I1 is I-1,
   chequearElementos(Tablero,T,I1,J).



%% Ejercicio 4

% asignarPeso(+Silueta, +Peso, -SiluetaConPeso).
% asignamos el peso a cada fila de la silueta con asignarPesoFila
asignarPeso([], _, []).
asignarPeso([Fila|FilaS], Peso, [Y|YS]) :-
   asignarPesoFila(Fila, Peso, Y),
   asignarPeso(FilaS, Peso, YS).


% asignarPesoFila asigna el peso a los atomos e ignora las variables
asignarPesoFila([], _, []).

asignarPesoFila([X|XS], Peso, [Peso|YS]) :-
   nonvar(X),
   asignarPesoFila(XS, Peso, YS).

asignarPesoFila([X|XS], Peso, [X|YS]) :-
   var(X),
   asignarPesoFila(XS, Peso, YS).



%% Ejercicio 5

% ubicarSilueta(+Silueta, -I, -J, -Tablero).
% En cada posicion I J del tablero verificamos si la silueta es ubicable.
% Usamos subtablero porque esUbicable verifica si la silueta se puede
% ubicar en la posicion 0 0 del tablero.
ubicarSilueta(S,I,J,T) :-
   posicion(T,I,J),
   subtablero(T,I,J,Sub),
   esUbicable(S,Sub).


esUbicable([],_).
esUbicable([X|XS], [Y|YS]) :-
   esUbicable(XS,YS),
   matchea(X,Y).


matchea([],_).
matchea([X|XS],[_|YS]):-
   var(X),
   matchea(XS,YS).

matchea([X|XS],[Y|YS]) :-
   nonvar(X),
   var(Y),
   X=Y,
   matchea(XS,YS).



%% Ejercicio 6

% ubicarPieza(+NombrePieza, +Peso, +DiccionarioPiezas, -I, -J, -Tablero).
% Una pieza se ubica en la posicion I J del Tablero si la pieza
% resultado de asignar el peso Peso a la silueta, que es el significado
% de la clave NombrePieza segun el diccionario DiccionarioPiezas,
% se puede ubicar en el tablero (en alguna orientacion).
ubicarPieza(NombrePieza, Peso, DiccPiezas, I, J, Tablero) :-
   getDicc(NombrePieza, DiccPiezas, L),
   member(S,L),
   asignarPeso(S, Peso, SP),
   ubicarSilueta(SP,I,J,Tablero).

% getDicc(+NombrePieza, +DiccPiezas, -listaOrientacionesPieza).
% Dado un diccionario, listaOrientacionesPieza es la lista de las orientaciones
% de la pieza de nombre NombrePieza segun ese diccionario.
getDicc(_, [], []).
getDicc(Pieza, [orientacion(Pieza, Y)|DiccS], [Y|L]) :-
   getDicc(Pieza, DiccS, L).
   
getDicc(Pieza, [orientacion(X, _)|DiccS], L) :-
   not(Pieza = X),
   getDicc(Pieza, DiccS, L).



%% Ejercicio 7

% solucionValida(-Juego).
% Una solucion es valida si todos los elementos del Tablero son numeros
% (es decir son variables instanciadas) y que las reglas de suma de
% columnas y filas se cumple
solucionValida(juego(Tablero, RF, RC)) :-
   sonTodosNumerosLL(Tablero),
   sumaColumnas(Tablero, RC),
   transponer(Tablero, T),
   sumaColumnas(T, RF).


sonTodosNumerosLL([]).
sonTodosNumerosLL([X|XS]) :-
   sonTodosNumerosL(X),
   sonTodosNumerosLL(XS).


sonTodosNumerosL([]).
sonTodosNumerosL([X|XS]) :-
   number(X),
   sonTodosNumerosL(XS).

% sumaColumnas(+X, -Y): Y es la lista resultado de sumar
% las listas de la lista X elemento a elemento.
% ej: sumaColumnas([[1,1,1,1], [1,1,1,1]], [2,2,2,2]).
sumaColumnas([X], X).
sumaColumnas([X|XS], Suma) :-
   XS \= [],
   sumaColumnas(XS, SumaXs),
   sumaListas(X, SumaXs, Suma).

% sumaListas([X1,X2,...,Xn], [Y1,Y2,...Yn], [X1+Y1, X2+Y2,..., Xn+Yn]).
sumaListas([], [], []).
sumaListas([X|XS], [Y|YS], [Sxy|Suma]) :-
   Sxy is (X + Y),
   sumaListas(XS, YS, Suma).



%% Ejercicio 8

% resolver(+DiccionarioPiezas, +PiezasDisponibles, -Juego).
% Un Juego que no tiene piezas por ubicar debe verificar
% que es una solucion valida.
resolver(_, [], Juego) :-
   solucionValida(Juego).

% Juego es el resultado de ubicar pieza por pieza (de las disponibles)
% en el tablero, en todas las posiciones ubicables.
resolver(DiccPiezas, [pieza(Nombre, Peso)|PiezasDisp], juego(Tablero, RF, RC)):-
   ubicarPieza(Nombre, Peso, DiccPiezas, _, _, Tablero),
   resolver(DiccPiezas, PiezasDisp, juego(Tablero, RF, RC)).